﻿using Dicom;
using Dicom.Imaging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Forms;

namespace MedVis_MidiController
{//The class to be enqueued.
    
    public class DicomQueFile : IComparable<DicomQueFile>
    {
        public string filename { get; private set; }

        //public FileStream file { get; private set; }
        public DicomImage image = null;
        public double Priority; // smaller values are higher priority
        public DicomQueFile(string filename)
        {
            this.filename = filename;
        }
        public int CompareTo(DicomQueFile other)
        {
            if (this.Priority < other.Priority) return -1;
            else if (this.Priority > other.Priority) return 1;
            else return 0;
        }
    }

    public class DicomImageDispatcher : IDisposable
    {
        bool disposed = false;
        public frmMain parent = null;
        private double WindowWidth = 2000;
        private double WindowCenter = 0;
        private bool WindowChanged = true;
        private bool ZoomChanged = true;

        System.Windows.Forms.Timer t1 = new System.Windows.Forms.Timer(); // Timer anlegen
        private BackgroundWorker backgroundWorkerImageProcessor = new BackgroundWorker();
        private BackgroundWorker backgroundWorkerFetcher = new BackgroundWorker();
        private BackgroundWorker backgroundWorkerFetcher2 = new BackgroundWorker();
        private BackgroundWorker backgroundWorkerFetcher3 = new BackgroundWorker();

        private List<DicomQueFile> priorityQueue { [MethodImpl(MethodImplOptions.Synchronized)]get; set; }
        private PictureBox targetPictureBox = null;
        private DicomQueFile[] dicomArr = null;

        private float _zoom = 1.0f;
        public float zoom
        {
            get
            {
                return _zoom;
            }
            set
            {
                if (this._zoom == value)
                    return;

                if (value < 0.05)
                    value = 0.05f;

                this.ZoomChanged = true;
                this._zoom = value;
            }

        }

        private int displayedIndex = -1;
        public int FilesCount { get; private set; }
        public int MaxIndex
        {
            get;
            [MethodImpl(MethodImplOptions.Synchronized)]
            private set;
        }
        private int _Index = -1;
        public int Index
        {
            get
            {
                return _Index;
            }
            set
            {
                if (MaxIndex == -1)
                    return;
                if( value >= MaxIndex || value <= 0 )
                {
                    if( value > MaxIndex )
                        value = MaxIndex;
                    if ( value <= 0 )
                        value = 0;
                }
                if (value == Index)
                    return;


                _Index = value;
                
            }
        }
        public DicomImageDispatcher()
        {
        }
        public DicomImageDispatcher(PictureBox pictureBox, string[] fileList, frmMain frm)
        {
            parent = frm;
            FilesCount = fileList.Count();
            targetPictureBox = pictureBox;
            
            t1.Interval = 100;
            t1.Tick += new EventHandler(t1_Tick);

            dicomArr = new DicomQueFile[FilesCount];

            for (int i = 0; i < FilesCount; i++)
            {
                DicomQueFile dFile = new DicomQueFile( fileList[i] );
                dicomArr[i] = dFile;
            }
            priorityQueue = new List<DicomQueFile>(dicomArr);
            MaxIndex = 0;
            Index = 0;
            zoom = 1.0f;

            backgroundWorkerImageProcessor.DoWork += 
                new DoWorkEventHandler(backgroundWorkerImageProcessor_DoWork);
            backgroundWorkerImageProcessor.RunWorkerCompleted += 
                new RunWorkerCompletedEventHandler(
            backgroundWorkerImageProcessor_RunWorkerCompleted);
            backgroundWorkerImageProcessor.WorkerSupportsCancellation = true;

            backgroundWorkerFetcher.DoWork +=
                new DoWorkEventHandler(backgroundWorkerFetcher_DoWork);
            backgroundWorkerFetcher.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
            backgroundWorkerFetcher_RunWorkerCompleted);
            backgroundWorkerFetcher.WorkerSupportsCancellation = true;

            backgroundWorkerFetcher2.DoWork +=
                new DoWorkEventHandler(backgroundWorkerFetcher_DoWork);
            backgroundWorkerFetcher2.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
            backgroundWorkerFetcher2_RunWorkerCompleted);
            backgroundWorkerFetcher2.WorkerSupportsCancellation = true;

            backgroundWorkerFetcher3.DoWork +=
                new DoWorkEventHandler(backgroundWorkerFetcher_DoWork);
            backgroundWorkerFetcher3.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
            backgroundWorkerFetcher3_RunWorkerCompleted);
            backgroundWorkerFetcher3.WorkerSupportsCancellation = true;

            backgroundWorkerFetcher.RunWorkerAsync();
            backgroundWorkerFetcher2.RunWorkerAsync();
            backgroundWorkerFetcher3.RunWorkerAsync();
            t1.Start();
        }

        private void backgroundWorkerFetcher_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (disposed)
                return;

            parent.Invoke(parent.progressBarChangerDelegate,
                        new Object[] { (1.0f - (float)priorityQueue.Count() / (float)(FilesCount + 1.0f)) });
            if (priorityQueue.Count() > 0)
                backgroundWorkerFetcher.RunWorkerAsync();

        }
        private void backgroundWorkerFetcher2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (disposed)
                return;

            parent.Invoke(parent.progressBarChangerDelegate,
                        new Object[] { (1.0f - (float)priorityQueue.Count() / (float)(FilesCount + 1.0f)) });
            if (priorityQueue.Count() > 0)
                backgroundWorkerFetcher2.RunWorkerAsync();

        }
        private void backgroundWorkerFetcher3_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (disposed)
                return;

            parent.Invoke(parent.progressBarChangerDelegate,
                        new Object[] { (1.0f - (float)priorityQueue.Count() / (float)(FilesCount + 1.0f)) });
            if (priorityQueue.Count() > 0)
                backgroundWorkerFetcher3.RunWorkerAsync();

        }

        private void backgroundWorkerFetcher_DoWork(object sender, DoWorkEventArgs e)
        {
            if ( disposed || priorityQueue.Count() <= 0)
            {
                return;
            }
            DicomQueFile dFile = priorityQueue[0];
            priorityQueue.Remove(dFile);

            if (dFile == null)
                return;

            if (dFile.image == null)
                loadDicom(dFile);

            MaxIndex = FilesCount - priorityQueue.Count() -1;
        }

        private void t1_Tick(object sender, EventArgs e)
        {
            t1.Stop();
            backgroundWorkerImageProcessor.RunWorkerAsync();
        }

        public void changeWindow(double windowCenter, double windowWidth)
        {
            WindowCenter = windowCenter;
            WindowWidth = windowWidth;
            WindowChanged = true;
        }

        private void loadDicom(DicomQueFile dFile)
        {
            try
            {
                dFile.image = new DicomImage(DicomFile.Open(dFile.filename).Dataset);
            }
            catch (DicomFileException exc)
            {
                return;
            }
            catch( Exception exc )
            {
                Console.WriteLine(exc);
            }

        }

        private void backgroundWorkerImageProcessor_DoWork(object sender, DoWorkEventArgs e)
        {
            if (Index != displayedIndex || WindowChanged || ZoomChanged)
            {

                DicomQueFile dFile = dicomArr[Index];

                if (dFile.image == null)
                    loadDicom(dFile);

                if (dFile.image == null)
                    return;

                displayedIndex = Index;
                WindowChanged = false;
                ZoomChanged = false;
                 dFile.image.WindowCenter = WindowCenter;
                 dFile.image.WindowWidth = WindowWidth;

                var image = dFile.image.RenderImage();
                
                 parent.Invoke(parent.imageChangerDelegate,
                        new Object[] { image });

                 return;
            }


        }
        private void backgroundWorkerImageProcessor_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if(!disposed)
             t1.Start();
        }

        ~DicomImageDispatcher()
        {
        }
        // Public implementation of Dispose pattern callable by consumers.
        public void Dispose()
        {
            Dispose(true);
        }
        // Protected implementation of Dispose pattern.
        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing)
            {
                if (backgroundWorkerFetcher.IsBusy)
                    backgroundWorkerFetcher.CancelAsync();
                if (backgroundWorkerImageProcessor.IsBusy)
                    backgroundWorkerImageProcessor.CancelAsync();
                t1.Dispose();
                priorityQueue = null;
                dicomArr = null;
            }

            disposed = true;
        }
    }
}
